-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: issue-5850 - Settle override conflicts between edges and propagate changes #7025
base: latest
Are you sure you want to change the base?
Conversation
This is going to need tests |
The edge's |
There are several bugs in the mechanism, and this is just the first fix. |
@wraithgar |
@wraithgar Anything else we need to do to merge this? |
@AlonNavon This looks awesome, really looking forward to having overrides work more the way you'd expect. With this change, will running |
@sgarfinkel The major bugs:
|
Just patience. Holidays are over and we got a lot of PRs. This PR is on the work board it's not been forgotten. |
Why was this closed? |
The PR wasn't made from the OP's fork, but presumably from their company's (which means that maintainers can't push to the PR branch, btw - always only make PRs from your own personal forks), and presumably someone at their company deleted the fork. |
Yeah, this was a mistake on our end. Our devops deleted the repo by accident. |
@@ -142,6 +181,24 @@ class OverrideSet { | |||
|
|||
return ruleset | |||
} | |||
|
|||
static findSpecificOverrideSet (first, second) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jdalton I moved the functions here, because fundamentally they are about comparing override sets, so this is the most natural location for them I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there anything else, or do you approve the PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AlonNavon
I think it looks great. Could you add code comments inline explaining the cases when the .silly is called (the one you changed from throwing to .silly). This will help folks if they see the logging know why.
Then the only other thing is adding any unit tests to cover some of the things you discovered and tweaked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @wraithgar, can you run the tests? To make sure nothing got accidentally broken with all the changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jdalton For the override set functions I created unit tests. I'm not sure how to translate the test cases that I'm running to unit tests.
This is the package.json of the simplest case:
{ "name": "test", "version": "1.0.0", "engines": { "npm": ">=8.3.0" }, "dependencies": { "json-server": "0.17.0" }, "overrides": { "json-server": { "package-json": "7.0.0" } } }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. @wraithgar I think this is where we'll need your help to push it over the line. (one last test case and a review). 🎉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heads up! Socket folks just noticed an issue with the lock files being generated and npm ci
.
In Socket's case the new patched version of npm
DOES produce a technically correct lock file, however the npm ci
command is expecting an incorrect lock file and errors when the correct lock file does NOT conform.
The issue is around dealing with bundled dependencies. For example, the package @socketregistry/deep-equal
has bundled dependencies which with this patch get correctly resolved and represented in the lock file. However, npm ci
expects the lock file to be layedout in a way which ignores the bundled dep and looks for the dep hoisted in the higher node_modules folder even though it DOES NOT exist on disk.
All of this to say, I think there is something in npm ci
that needs checking in on (I believe it does do a form of npm install
). The workaround for Socket was to do the "fixed" install and then run npm install --ignore-scripts --package-lock-only
to put back the incorrect, but expected by npm ci
, lock entries. The --ignore-scripts --package-lock-only
worked on the patched npm
too. So something to investigate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AlonNavon Any insight into the issue above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jdalton @AlonNavon I just tried to reproduce this package-lock.json issue and wasn't able to. Using the patched and unpatched version gave me the same package-lock. I did notice that the socketregistry/deep-equal
has a number of scripts, including update-package-lock.js
which modifies the package-lock.json directly and forces another npm install
.
👋 We are shipping npm 11 this morning and will be able to focus on this issue again. This is a priority task for us this next quarter. |
Had to close/reopen to get the "approve" button to show up again. |
That's great news! I really hope we can get this fix deployed soon 👍 |
@@ -103,7 +104,7 @@ class Edge { | |||
} | |||
|
|||
satisfiedBy (node) { | |||
if (node.name !== this.#name) { | |||
if (node.name !== this.#name || !this.#from) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious about this check. The constructor will throw a TypeError
if from
is falsey - do we need this and added checks to this.from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently the ArboristEdge
constructor does NOT throw and only assigns a this.from
if edgeFrom != null
(null
or undefined
). The Edge
constructor uses ArboristEdge
and DOES throw in its constructor if if (!from) {
.
If the !this.#from
isn't needed then the if (edgeFrom != null) {
of ArboristEdge
isn't needed either.
t.equal(OverrideSet.findSpecificOverrideSet(overrides5, overrides5), overrides5, "find more specific override set when the sets are identical") | ||
t.equal(OverrideSet.findSpecificOverrideSet(overrides5, overrides6), overrides6, "find more specific override set when it's the second") | ||
t.equal(OverrideSet.findSpecificOverrideSet(overrides6, overrides5), overrides6, "find more specific override set when it's the first") | ||
t.ok(!OverrideSet.doOverrideSetsConflict(overrides1, overrides2), "override sets are equal") | ||
t.ok(!OverrideSet.doOverrideSetsConflict(overrides5, overrides5), "override sets are the same object") | ||
t.ok(!OverrideSet.doOverrideSetsConflict(overrides5, overrides6), "one override set is the specific version of the other") | ||
t.ok(!OverrideSet.doOverrideSetsConflict(overrides6, overrides5), "one override set is the specific version of the other") | ||
t.ok(OverrideSet.doOverrideSetsConflict(overrides5, overrides7), "no override set is the specific version of the other") | ||
t.ok(OverrideSet.doOverrideSetsConflict(overrides7, overrides5), "no override set is the specific version of the other") | ||
t.ok(!overrides7.isEqual(overrides8), 'two override sets that differ in the version are not equal') | ||
t.ok(!overrides8.isEqual(overrides9), 'two override sets that differ in the range are not equal') | ||
t.ok(!overrides7.isEqual(overrides9), 'two override sets that differ in both version and range are not equal') | ||
t.ok(OverrideSet.doOverrideSetsConflict(overrides7, overrides8), "override sets are incomparable due to version") | ||
t.ok(OverrideSet.doOverrideSetsConflict(overrides7, overrides9), "override sets are incomparable due to version and range") | ||
t.ok(OverrideSet.doOverrideSetsConflict(overrides8, overrides9), "override sets are incomparable due to range") | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
t.equal(OverrideSet.findSpecificOverrideSet(overrides5, overrides5), overrides5, "find more specific override set when the sets are identical") | |
t.equal(OverrideSet.findSpecificOverrideSet(overrides5, overrides6), overrides6, "find more specific override set when it's the second") | |
t.equal(OverrideSet.findSpecificOverrideSet(overrides6, overrides5), overrides6, "find more specific override set when it's the first") | |
t.ok(!OverrideSet.doOverrideSetsConflict(overrides1, overrides2), "override sets are equal") | |
t.ok(!OverrideSet.doOverrideSetsConflict(overrides5, overrides5), "override sets are the same object") | |
t.ok(!OverrideSet.doOverrideSetsConflict(overrides5, overrides6), "one override set is the specific version of the other") | |
t.ok(!OverrideSet.doOverrideSetsConflict(overrides6, overrides5), "one override set is the specific version of the other") | |
t.ok(OverrideSet.doOverrideSetsConflict(overrides5, overrides7), "no override set is the specific version of the other") | |
t.ok(OverrideSet.doOverrideSetsConflict(overrides7, overrides5), "no override set is the specific version of the other") | |
t.ok(!overrides7.isEqual(overrides8), 'two override sets that differ in the version are not equal') | |
t.ok(!overrides8.isEqual(overrides9), 'two override sets that differ in the range are not equal') | |
t.ok(!overrides7.isEqual(overrides9), 'two override sets that differ in both version and range are not equal') | |
t.ok(OverrideSet.doOverrideSetsConflict(overrides7, overrides8), "override sets are incomparable due to version") | |
t.ok(OverrideSet.doOverrideSetsConflict(overrides7, overrides9), "override sets are incomparable due to version and range") | |
t.ok(OverrideSet.doOverrideSetsConflict(overrides8, overrides9), "override sets are incomparable due to range") | |
t.equal(OverrideSet.findSpecificOverrideSet(overrides5, overrides5), overrides5, 'find more specific override set when the sets are identical') | |
t.equal(OverrideSet.findSpecificOverrideSet(overrides5, overrides6), overrides6, "find more specific override set when it's the second") | |
t.equal(OverrideSet.findSpecificOverrideSet(overrides6, overrides5), overrides6, "find more specific override set when it's the first") | |
t.ok(!OverrideSet.doOverrideSetsConflict(overrides1, overrides2), 'override sets are equal') | |
t.ok(!OverrideSet.doOverrideSetsConflict(overrides5, overrides5), 'override sets are the same object') | |
t.ok(!OverrideSet.doOverrideSetsConflict(overrides5, overrides6), 'one override set is the specific version of the other') | |
t.ok(!OverrideSet.doOverrideSetsConflict(overrides6, overrides5), 'one override set is the specific version of the other') | |
t.ok(OverrideSet.doOverrideSetsConflict(overrides5, overrides7), 'no override set is the specific version of the other') | |
t.ok(OverrideSet.doOverrideSetsConflict(overrides7, overrides5), 'no override set is the specific version of the other') | |
t.ok(!overrides7.isEqual(overrides8), 'two override sets that differ in the version are not equal') | |
t.ok(!overrides8.isEqual(overrides9), 'two override sets that differ in the range are not equal') | |
t.ok(!overrides7.isEqual(overrides9), 'two override sets that differ in both version and range are not equal') | |
t.ok(OverrideSet.doOverrideSetsConflict(overrides7, overrides8), 'override sets are incomparable due to version') | |
t.ok(OverrideSet.doOverrideSetsConflict(overrides7, overrides9), 'override sets are incomparable due to version and range') | |
t.ok(OverrideSet.doOverrideSetsConflict(overrides8, overrides9), 'override sets are incomparable due to range') |
// The override sets are incomparable. Neither one contains the other. | ||
log.silly('Conflicting override sets', first, second) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A first step in fixing the overrides feature. In this PR I'm aiming to fix 3 bugs:
So if we have dependency chains A->B->C and A->C, and we override C under B, then C will be overridden.
References
Fixes some of the override issues.